/*
 * Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2023-2023. All rights reserved.
 */

#include "TcpPortWork.h"
#include "winsock.h"
using namespace SerialPortClass;
namespace  {
    const int RECEIVE_SLEEP_TIME = 2;
    const int WRITE_TIME_OUT = 10;
    const int READ_TIME_OUT = 2;
    const int SOCKET_VERSION_LOW_BYTE = 2;
    const int SOCKET_VERSION_HIGH_BYTE = 2;
    const int SOCKET_CONNECT_TIMEOUT = 2;
    const unsigned long SOCKET_NON_BLOCK_MODE = 1;
    const unsigned long SOCKET_BLOCK_MODE = 0;
}
TcpPortWork::TcpPortWork(QObject *parent) : QThread(parent),
    witeThreadMsgId(0),
    oneTimeReadByte(0),
    writeBuffer(nullptr),
    writeSize(1),
    alreadyReadByte(0),
    writeBufferSize(0),
    isThreadAlive(false)
{
    SecureZeroMemory(rxBuffer, sizeof(rxBuffer));
}

TcpPortWork::~TcpPortWork() {
}

bool TcpPortWork::Connect(QString ip, UINT port)
{
    // 设置套接字为非阻塞模式连接
    unsigned long mode = SOCKET_NON_BLOCK_MODE;
    if (ioctlsocket(m_sockClient, FIONBIO, &mode) == SOCKET_ERROR) {
        return false;
    }

    sockaddr_in addrInfo = { 0 };
    addrInfo.sin_family = AF_INET;
    addrInfo.sin_addr.S_un.S_addr = inet_addr(ip.toStdString().c_str());
    if (addrInfo.sin_addr.S_un.S_addr == INADDR_NONE) {
        return false;
    }
    addrInfo.sin_port = htons(static_cast<unsigned short>(port));

    int connectResult = ::connect(m_sockClient, reinterpret_cast<sockaddr*>(&addrInfo), sizeof(sockaddr_in));
    if (connectResult == SOCKET_ERROR) {
        struct timeval timeout;
        timeout.tv_sec = SOCKET_CONNECT_TIMEOUT;
        fd_set writefds;

        if (WSAGetLastError() == WSAEWOULDBLOCK) {
            FD_ZERO(&writefds);
            FD_SET(m_sockClient, &writefds);
            // 超时时间内是否有可写事件
            if (select(m_sockClient + 1, nullptr, &writefds, nullptr, &timeout) < 0) {
                return false;
            }
            // 套接字是否可写
            if (!FD_ISSET(m_sockClient, &writefds)) {
                return false;
            }
            return true;
        } else {
            return false;
        }
    }
    // 设置套接字为阻塞模式发送
    mode = SOCKET_BLOCK_MODE;
    if (ioctlsocket(m_sockClient, FIONBIO, &mode) == SOCKET_ERROR) {
        return false;
    }
    return true;
}

bool TcpPortWork::CreateSocket(QString ip, UINT port)
{
    unsigned short sockVersion = MAKEWORD(SOCKET_VERSION_LOW_BYTE, SOCKET_VERSION_HIGH_BYTE);
    WSADATA wsaData;
    if (WSAStartup(sockVersion, &wsaData) != 0) {
        return false;
    }
    m_sockClient = socket(AF_INET, SOCK_STREAM, 0);
    if (m_sockClient == INVALID_SOCKET) {
        return false;
    }
    int writeTimeout = WRITE_TIME_OUT;
    if (setsockopt(m_sockClient, SOL_SOCKET, SO_SNDTIMEO,
        reinterpret_cast<char*>(&writeTimeout), sizeof(int)) != 0) {
        return false;
    }
    int readTimeout = READ_TIME_OUT;
    if (setsockopt(m_sockClient, SOL_SOCKET, SO_RCVTIMEO,
        reinterpret_cast<char*>(&readTimeout), sizeof(int)) != 0) {
        return false;
    }
    int nRecvBuf = RX_BUFFER_SIZE;
    if (setsockopt(m_sockClient, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<char*>(&nRecvBuf),
        sizeof(int)) != 0) {
        return false;
    }
    if (!Connect(ip, port)) {
        return false;
    }
    return true;
}

bool TcpPortWork::InitPort(PortBaseInitStru &tcpPortInit)
{
    if (isThreadAlive) {
        this->quit();
        if (this->wait()) {
            isThreadAlive = false;
        }
    }
    SecureZeroMemory(rxBuffer, sizeof(rxBuffer));

    if (writeBuffer != nullptr) {
        delete[] writeBuffer;
        writeBuffer = nullptr;
    }
    if (tcpPortInit.writebuffersize == 0) {
        return false;
    }
    writeBuffer = new char[tcpPortInit.writebuffersize];
    oneTimeReadByte = tcpPortInit.oneTimeReadByte;
    writeBufferSize = tcpPortInit.writebuffersize;

    bool result = CreateSocket(tcpPortInit.ipAddr, tcpPortInit.ipPort);
    if (!result) {
        ClosePort();
    }
    return result;
}

void TcpPortWork::SlotWriteToPort(const char *string, int n)
{
    if (writeBuffer == nullptr || string == nullptr || memcpy_s(writeBuffer, writeBufferSize, string, n) != 0) {
        return;
    }
    writeSize = n;
    WriteChar();
}

void TcpPortWork::WriteChar()
{
    if (m_sockClient != INVALID_SOCKET) {
        isReceive = false;
        send(m_sockClient, reinterpret_cast<const char*>(writeBuffer), writeSize, 0);
        isReceive = true;
    }
}

bool TcpPortWork::IsThreadAlive() const
{
    return isThreadAlive;
}

void TcpPortWork::SetThreadAlive(bool threadAlive)
{
    this->isThreadAlive = threadAlive;
}

void TcpPortWork::SetOneTimeReadByte(DWORD readByte)
{
    this->oneTimeReadByte = readByte;
}

void TcpPortWork::ClosePort()
{
    if (m_sockClient != INVALID_SOCKET) {
        if (closesocket(m_sockClient) != INVALID_SOCKET) {
            m_sockClient = INVALID_SOCKET;
        }
    }
}

void TcpPortWork::ReportBuffer()
{
    int txBuffSize = alreadyReadByte;
    if (alreadyReadByte > RX_BUFFER_SIZE) {
        txBuffSize = RX_BUFFER_SIZE;
    }
    auto txBuff = new unsigned char[txBuffSize];
    if (txBuff != nullptr) {
        if (memcpy_s(txBuff, txBuffSize, rxBuffer, txBuffSize) != 0) {
            return;
        }
        SecureZeroMemory(rxBuffer, alreadyReadByte);
        emit SignalReadData(reinterpret_cast<WPARAM>(txBuff), static_cast<LPARAM>(alreadyReadByte));
    }
}

void TcpPortWork::run()
{
    isThreadAlive = true;
    while (1) {
        Sleep(RECEIVE_SLEEP_TIME);
        if (m_sockClient == INVALID_SOCKET) {
            break;
        }
        if (sizeof(rxBuffer) - alreadyReadByte == 0) {
            continue;
        }
        if (!isReceive) {
            continue;
        }
        uint64_t avaiableLen = sizeof(rxBuffer) - alreadyReadByte;
        int bResult = ::recv(m_sockClient, rxBuffer + alreadyReadByte,  avaiableLen, 0);
        if (bResult > 0) {
            alreadyReadByte += bResult;
        } else {
            continue;
        }

        if (alreadyReadByte <= oneTimeReadByte) {
            continue;
        }
        if (alreadyReadByte <= 0) {
            continue;
        }
        ReportBuffer();
        alreadyReadByte = 0;
    }
}
